package com.fanrui.code.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import com.fanrui.code.util.logger.Stdout;

/**
 * 实现游戏服务器中的主逻辑循环（支持定时任务） 游戏内为保证单一线程（或固定数量线程）执行所有逻辑，应放弃直接使用Executors，而使用此处的定时器。
 * 
 *
 */
public class MainLoopPlus {

	@Override
	public String toString() {
		return "MainLogicLoopPlus [name=" + name + "]";
	}

	private ScheduledExecutorService threadPool;

	private String name; // （用于命名线程）方便线上问题调试

	public MainLoopPlus(String name) {
		this(name, 1); // 缺省单一线程
	}

	public MainLoopPlus(String name, int threads) {
		this.name = name;
		this.threadPool = Executors.newScheduledThreadPool(threads, new NamedThreadFactory(this.name + "-"));
	}

	/**
	 * 
	 * 为防止Runnable.run()过程中出现的异常，导致终止后续定时任务，这里包装一下。 https://segmentfault.com/a/1190000000371905
	 * 
	 *
	 */
	public static abstract class StrongRunnable implements Runnable {

		@Override
		final public void run() {
			try {
				strongRun();
			} catch (Throwable t) {
				// 注意这里吃掉了异常，是为了保证定时任务不会被终止。
				Stdout.GET().error("执行［" + toString() + "］过程中出现错误！", t);
			}
		}

		protected abstract void strongRun() throws Exception;
	}

	/**
	 * 为防止Runnable.run()过程中出现的异常被隐藏掉，这里需要做LOG。 https://my.oschina.net/lifany/blog/884002
	 * 
	 *
	 */
	public static abstract class LogExceptionRunnable implements Runnable {

		@Override
		final public void run() {
			try {
				logExceptionRun();
			} catch (Throwable t) {
				// 注意这里吃掉了异常，是为了保证定时任务不会被终止。
				Stdout.GET().error("执行［" + toString() + "］过程中出现错误！", t);
				t.printStackTrace();
				throw new RuntimeException(t);
			}
		}

		protected abstract void logExceptionRun() throws Exception;
	}

	/**
	 * 
	 * 为防止Callable.run过程中出现的异常被隐藏掉（没有调用返回的Future的get()方法），这里包装一下。
	 * 
	 *
	 * @param <T>
	 */
	public static abstract class LogExceptionCallable<T> implements Callable<T> {
		@Override
		final public T call() throws Exception {
			try {
				return logExceptionCall();
			} catch (Exception e) {
				// 为防止出了异常被隐藏掉，这里先输出LOG。
				Stdout.GET().error("执行［" + toString() + "］过程中出现错误！", e);
				e.printStackTrace();
				throw e;
			}
		}

		protected abstract T logExceptionCall() throws Exception;
	}

	/**
	 * 向主循环中放入待执行的任务，立即执行
	 * 
	 * @param task
	 */
	public void execute(LogExceptionRunnable task) {
		this.threadPool.execute(task); // 需要防止异常被吃掉
	}

	/**
	 * 向主循环中放入待执行的任务，立即执行
	 * 
	 * @param task
	 * @return
	 */
	public <T> Future<T> submit(LogExceptionCallable<T> task) {
		return threadPool.submit(task); // 需要防止异常被吃掉
	}

	/**
	 * 向主循环中放入待执行的任务，立即执行
	 * 
	 * @param task
	 * @param result
	 * @return
	 */
	public <T> Future<T> submit(LogExceptionRunnable task, T result) {
		return threadPool.submit(task, result); // 需要防止异常被吃掉
	}

	/**
	 * 向主循环中放入待执行的任务，立即执行
	 * 
	 * @param task
	 * @return
	 */
	public Future<?> submit(LogExceptionRunnable task) {
		return threadPool.submit(task); // 需要防止异常被吃掉
	}

	/**
	 * 向主循环中放入待处理的任务，延迟执行
	 * 
	 * @param task
	 * @param delay
	 * @param unit
	 * @return
	 */
	public ScheduledFuture<?> schedule(LogExceptionRunnable task, long delay, TimeUnit unit) {
		return threadPool.schedule(task, delay, unit); // 需要防止异常被吃掉
	}

	/**
	 * 向主循环中放入待处理的任务，延迟执行
	 * 
	 * @param task
	 * @param delay
	 * @param unit
	 * @return
	 */
	public <V> ScheduledFuture<V> schedule(LogExceptionCallable<V> task, long delay, TimeUnit unit) {
		return threadPool.schedule(task, delay, unit); // 需要防止异常被吃掉
	}

	/**
	 * 向主循环中放入待处理的任务，以固定时间间隔启动（执行）
	 * 
	 * @param task
	 * @param initialDelay
	 * @param period
	 * @param unit
	 * @return
	 */
	public ScheduledFuture<?> scheduleAtFixedRate(StrongRunnable task, long initialDelay, long period, TimeUnit unit) {
		return threadPool.scheduleAtFixedRate(task, initialDelay, period, unit); // 需要防止异常导致定时器终止
	}

	/**
	 * 向主循环中放入待处理的指令，执行后间隔时间固定
	 * 
	 * @param task
	 * @param initialDelay
	 * @param delay
	 * @param unit
	 * @return
	 */
	public ScheduledFuture<?> scheduleWithFixedDelay(StrongRunnable task, long initialDelay, long delay,
			TimeUnit unit) {
		return threadPool.scheduleWithFixedDelay(task, initialDelay, delay, unit); // 需要防止异常导致定时器终止
	}

	/**
	 * 向主循环中放入帧逻辑任务。
	 * 
	 * @param task
	 * @param initialDelay
	 * @param delay
	 * @param unit
	 * @return
	 */
	public ScheduledFuture<?> addFrameLogicTask(FrameTask task) {
		if (task.isFixedRateOrFixedDelay()) {
			return scheduleAtFixedRate(task, task.getInitialDelay(), task.getDelay(), task.getUnit());
		} else {
			return scheduleWithFixedDelay(task, task.getInitialDelay(), task.getDelay(), task.getUnit());
		}
	}

	/**
	 * 停止主循环
	 */
	public void shutdown() {
		this.threadPool.shutdown();
	}

	/**
	 * 是否真的停止完毕了
	 * 
	 * @return
	 */
	public boolean isTerminated() {
		return this.threadPool.isTerminated();
	}

	/**
	 * 停止并等待结束
	 * 
	 * @throws InterruptedException
	 */
	public void stopAndWait() throws InterruptedException {
		this.threadPool.shutdown();
		while (!this.threadPool.isTerminated()) {
			Thread.sleep(100);
		}
	}

	//
	// test
	//
	public static void main(String[] args) {
		// MainLoopPlus mainLoop = new MainLoopPlus("MainLoopPlus-Test");
		// mainLoop.execute(new LogExceptionRunnable() {
		//
		// @Override
		// protected void logExceptionRun() throws Exception {
		// throw new RuntimeException("测试异常");
		// }
		// });
		// mainLoop.execute(new Runnable() {
		//
		// @Override
		// public void run() {
		// throw new RuntimeException("测试异常");
		// }
		// });
	}
}
